//==UserScript==
//@name           KoC Allianz - Extras
//@namespace      PDX
//@description    Extra Tabs für die Allianz und Allianz Interne Links
//@homepage	   http://koc.god-like.org
//@version  	   0.3.6

//@include        http://*.kingdomsofcamelot.com/*main_src.php*
//@include        http://apps.facebook.com/kingdomsofcamelot/*

//@icon		   http://koc.god-like.info/pdx.jpg

//@require       http://koc.god-like.info/update/kocallianzextras.php?id=98788
//==/UserScript==

var Version = '0.3.6';
var Versiondate = '17.04.2011';
var Scriptname = 'KoC Allianz - Extras';
var ScriptTabName = "'+ getMyAlliance()[1] +'";

var DEBUG_TRACE = false;
var ENABLE_ATTACK_TAB = false;
var ENABLE_SAMPLE_TAB = false;

var DISABLE_BULKADD_LIST = false;
var ENABLE_GM_AJAX_TRACE = false;

//end test switches

var MAP_DELAY = 1200;

var URL_CASTLE_BUT = 'http://koc.god-like.info/schloss1.png';
var URL_CASTLE_BUT_SEL = 'http://koc.god-like.info/schloss2.png';
//var CHAT_BG_IMAGE = 'http://i.imgur.com/VjNst.jpg';   // 600
var CHAT_BG_IMAGE = 'http://koc.god-like.info/chatbg.png';   // 720



var JSON;if(!JSON){JSON={};}(function(){"use strict";function f(n){return n<10?'0'+n:n;}if(typeof Date.prototype.toJSON!=='function'){Date.prototype.toJSON=function(key){return isFinite(this.valueOf())?this.getUTCFullYear()+'-'+f(this.getUTCMonth()+1)+'-'+f(this.getUTCDate())+'T'+f(this.getUTCHours())+':'+f(this.getUTCMinutes())+':'+f(this.getUTCSeconds())+'Z':null;};String.prototype.toJSON=Number.prototype.toJSON=Boolean.prototype.toJSON=function(key){return this.valueOf();};}var cx=/[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,escapable=/[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,gap,indent,meta={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'},rep;function quote(string){escapable.lastIndex=0;return escapable.test(string)?'"'+string.replace(escapable,function(a){var c=meta[a];return typeof c==='string'?c:'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);})+'"':'"'+string+'"';}function str(key,holder){var i,k,v,length,mind=gap,partial,value=holder[key];if(value&&typeof value==='object'&&typeof value.toJSON==='function'){value=value.toJSON(key);}if(typeof rep==='function'){value=rep.call(holder,key,value);}switch(typeof value){case'string':return quote(value);case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}gap+=indent;partial=[];if(Object.prototype.toString.apply(value)==='[object Array]'){length=value.length;for(i=0;i<length;i+=1){partial[i]=str(i,value)||'null';}v=partial.length===0?'[]':gap?'[\n'+gap+partial.join(',\n'+gap)+'\n'+mind+']':'['+partial.join(',')+']';gap=mind;return v;}if(rep&&typeof rep==='object'){length=rep.length;for(i=0;i<length;i+=1){k=rep[i];if(typeof k==='string'){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}else{for(k in value){if(Object.hasOwnProperty.call(value,k)){v=str(k,value);if(v){partial.push(quote(k)+(gap?': ':':')+v);}}}}v=partial.length===0?'{}':gap?'{\n'+gap+partial.join(',\n'+gap)+'\n'+mind+'}':'{'+partial.join(',')+'}';gap=mind;return v;}}if(typeof JSON.stringify!=='function'){JSON.stringify=function(value,replacer,space){var i;gap='';indent='';if(typeof space==='number'){for(i=0;i<space;i+=1){indent+=' ';}}else if(typeof space==='string'){indent=space;}rep=replacer;if(replacer&&typeof replacer!=='function'&&(typeof replacer!=='object'||typeof replacer.length!=='number')){throw new Error('JSON.stringify');}return str('',{'':value});};}if(typeof JSON.parse!=='function'){JSON.parse=function(text,reviver){var j;function walk(holder,key){var k,v,value=holder[key];if(value&&typeof value==='object'){for(k in value){if(Object.hasOwnProperty.call(value,k)){v=walk(value,k);if(v!==undefined){value[k]=v;}else{delete value[k];}}}}return reviver.call(holder,key,value);}text=String(text);cx.lastIndex=0;if(cx.test(text)){text=text.replace(cx,function(a){return'\\u'+('0000'+a.charCodeAt(0).toString(16)).slice(-4);});}if(/^[\],:{}\s]*$/.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof reviver==='function'?walk({'':j},''):j;}throw new SyntaxError('JSON.parse');};}}());
var JSON2 = JSON; 

//logit ("+++ STARTUP: "+ document.URL);

GlobalOptions = JSON2.parse (GM_getValue ('Options_??', '{}'));

if (document.URL.search(/apps.facebook.com\/kingdomsofcamelot/i) >= 0){
	facebookInstance ();

}


/***  Run only in "apps.facebook.com" instance ... ***/
function facebookInstance (){
	function setWide (){
		var iFrame = null;

		var e = document.getElementById('app_content_130402594779').firstChild.firstChild;
		for (var c=0; c<e.childNodes.length; c++){
			if (e.childNodes[c].tagName=='SPAN' && e.childNodes[c].firstChild.className == 'canvas_iframe_util'){
				iFrame = e.childNodes[c].firstChild; 
				break;
			}
		}
		if (!iFrame){
			var iframes = document.getElementsByTagName('iframe');
			for (var i=0; i<iframes.length; i++){
				if (iframes[i].className=='canvas_iframe_util'){
					iFrame = iframes[i];
					break; 
				}
			}
		}
		if (!iFrame){
			setTimeout (setWide, 1000);
			return;
		}
		try{    
			document.getElementById('sidebar_ads').parentNode.removeChild(document.getElementById('sidebar_ads'));
			document.getElementById('canvas_nav_content').parentNode.removeChild(document.getElementById('canvas_nav_content'));
		} catch (e){
			// toolkit may have removed them already!
		}
		var e = document.getElementById('content').firstChild;
		e.style.width = '100%';
		e.firstChild.style.width = '100%';
		iFrame.style.width = '100%';
	}

	facebookWatchdog();
	if (GlobalOptions.pcWideScreen)
		setWide();
}



var Options = {
		includeMarching:true,
		encRemaining : true,
		maxIdlePop   : false,	
		srcSortBy    : 'level',
		srcMinLevel  : 1,
		srcMaxLevel  : 7,
		wildType     : 1,
		unownedOnly  : true,
		pcWinIsOpen  : false,
		pcWinDrag    : true,
		pcWinPos     : {},
		pcTrackOpen  : true,
		pcKillFairie : false,
		pcGoldHappy  : 95,
		pcGoldEnable : false,
		pcEveryEnable: false,
		pcEveryMins  : 30,
		pcChatOnRight: false,
		pcWideMap    : false,
		giftDomains  : {valid:false, list:{}},
		giftDelete   : 'e',
		fixTower     : true,
		fixTower2    : true,
		fixMapDistance: true,
		fixMsgCount  : true,
		enhanceARpts : true,
		allowAlterAR : true,
		enhanceMsging : true,
		ptWinIsOpen  : false,
		ptWinDrag    : false,
		ptWinPos     : {},
		enableFoodWarn : true,
		foodWarnHours : 24,
		alertConfigpdx  : {
			aChat:true, 
			aPrefix:'-= ACHTUNG = ANGRIFF =-', 
			scouting:true, 
			wilds:true, 
			minTroops:1, 
			spamLimit:10, 
			embassy:true, 
			mytroops:true, 
			food:true, 
			defense:true, 
			hq:'',
			sendEmail:true,
			emailAddress:'',
			token:'',
			sendasWhisper:true,
			sendtoAlly:true,
		},
};
unsafeWindow.pt_Options=Options;

var GlobalOptions = {
		pcWatchdog   : false,
		pcWideScreen : true,
};

var Cities = {};
var Seed = unsafeWindow.seed;
var Tabs = {};
var mainPop;
var pcStartupTimer = null;
var CPopUpTopClass = 'pcPopTop';


pcStartup ();

function pcStartup (){
	window.clearTimeout (pcStartupTimer);
	if (document.getElementById('pcOfficial') != null)
		return;
	var metd = getClientCoords(document.getElementById('main_engagement_tabs'));
	if (metd.width==null || metd.width==0){
		pcStartupTimer = window.setTimeout (pcStartup, 1000);
		return;
	}

	Seed = unsafeWindow.seed;
	var styles = '.xtab {padding-right: 5px; border:none; background:none; white-space:nowrap;}\
		.xtabBR {padding-right: 5px; border:none; background:none;}\
		table.pcTab tr td {border:none; background:none; white-space:nowrap; padding:0px}\
		table.pcTabPadNW tr td {border:none; background:none; white-space:nowrap; padding: 2px 4px 2px 8px;}\
		table.pcTabBR tr td {border:none; background:none;}\
		table.pcTabLined tr td {border:1px none none solid none; padding: 2px 5px; white-space:nowrap;}\
		table.pcTabPad tr td.pcentry {background-color:none; padding-left: 8px;}\
		table.ptNoPad tr td {border:none; background:none; white-space:nowrap; padding:0px}\
		.pcstat {border:1px solid; border-color:#ffffff; font-weight:bold; padding-top:2px; padding-bottom:2px; text-align:center; color:#ffffff; background-color:#357}\
		.pcStatLight {color:#ddd}\
		.pcentry {padding: 7px; border:1px solid; border-color:#000000; background-color:#ffeecc; white-space:nowrap;}\
		.ptErrText {font-weight:bold; color:#600000}\
		.castleBut {outline:0px; margin-left:0px; margin-right:0px; width:24px; height:26px; font-size:12px; font-weight:bold;}\
		.castleBut:hover {border-size:3px; border-color:#000;}\
		button::-moz-focus-inner, input[type="submit"]::-moz-focus-inner { border: none; }\
		span.whiteOnRed {padding-left:3px; padding-right:3px; background-color:#700; color:white; font-weight:bold}\
		span.boldRed {color:#800; font-weight:bold}\
		.castleButNon {background-image:url("'+ URL_CASTLE_BUT +'")}\
		.castleButSel {background-image:url("'+ URL_CASTLE_BUT_SEL +'")}\
		a.ptButton20 {color:#ffff80}\
		table.pcMainTab {empty-cells:show; margin-top:5px }\
		table.pcMainTab tr td a {color:inherit }\
		table.pcMainTab tr td   {height:60%; empty-cells:show; padding: 0px 5px 0px 5px;  margin-top:5px; white-space:nowrap; border: 1px solid; border-style: none none solid none; }\
		table.pcMainTab tr td.spacer {padding: 0px 4px;}\
		table.pcMainTab tr td.sel    {font-weight:bold; font-size:13px; border: 1px solid; border-style: solid solid none solid; background-color:#eed;}\
		table.pcMainTab tr td.notSel {font-weight:bold; font-size:13px; border: 1px solid; border-style: solid solid none solid; background-color:#DF7401; color:white; border-color:black;}\
		tr.pcPopTop td { background-color:#FFFFFF; border:none; height: 21px;  padding:0px; }\
		tr.pcretry_pcPopTop td { background-color:#FF0000; color:#fff; border:none; height: 21px;  padding:0px; }\
		.CPopup .CPopMain { background-color:#f8f8f8; padding:6px;}\
		.CPopup  {border:3px ridge #666}\
		div.indent25 {padding-left:25px}';


	window.name = 'PDXAE';
	logit ("* "+Scriptname +" by PDX v"+ Version +" geladen!");
	readOptions();
	readGlobalOptions ();
	setCities();

//	TODO: Make sure WinPos is visible on-screen ?
	if (Options.pcWinPos==null || Options.pcWinPos.x==null|| Options.pcWinPos.x=='' || isNaN(Options.pcWinPos.x)){
		var c = getClientCoords (document.getElementById('main_engagement_tabs'));
		Options.pcWinPos.x = c.x+4;
		Options.pcWinPos.y = c.y+c.height;
		saveOptions ();
	}
	mainPop = new CPopup ('pc', Options.pcWinPos.x, Options.pcWinPos.y, 750,750, Options.pcWinDrag, 
			function (){
		tabManager.hideTab();
		Options.pcWinIsOpen=false; 
		saveOptions()
	});
	mainPop.autoHeight (true);  

	mainPop.getMainDiv().innerHTML = '<STYLE>'+ styles +'</style>';
	tabManager.init (mainPop.getMainDiv());
	actionLog (""+Scriptname +" by PDX v"+ Version +" geladen!");


	//FairieKiller.init (Options.pcKillFairie);
	//RefreshEvery.init ();
	//CollectGold.init();

	if (Options.pcWinIsOpen && Options.pcTrackOpen){
		mainPop.show (true);
		tabManager.showTab();
	}
	window.addEventListener('unload', onUnload, false);
	TowerAlerts.init();
	TowerAlerts.setPostToChatOptions(Options.alertConfigpdx);
	AddMainTabLink(''+ getMyAlliance()[1] + '', eventHideShow, mouseMainTab);
	kocWatchdog ();
}
/*********************************** Options Tab ***********************************/
Tabs.Options = {
		tabOrder: 20,
		tabLabel : 'Einstellung',
		myDiv : null,
		fixAvailable : {},

		init : function (div){
			var t = Tabs.Options;
			t.myDiv = div;


			// CitySelect
			var citySelect = '   <SELECT id=pcalertHQ class=pcalertHQ name=pcalertHQ   onchange="saveOptions();">"';

			for (var c=0; c<Cities.numCities; c++) {

				aCity = Cities.cities[c].name + ' ('+Cities.cities[c].x + ','+ Cities.cities[c].y+')';
				citySelect += '<option value=\''+ aCity +'\' '+ (Options.alertConfigpdx.hq==aCity?' SELECTED ':'') +'>'+aCity+'</option>';
			}   
			citySelect += '</select>';
			try {      
				m = '<DIV style="height:550px; max-height:550px; overflow-y:auto"><TABLE width=auto class=pcTabLined cellspacing=0 cellpadding=0>\
					<TR><TD colspan=2><B>Wachturm - Einstellung</b></td></tr>\
					<TR><TD><INPUT id=pcalertEnable type=checkbox '+ (Options.alertConfigpdx.aChat?' ':'') +'/></td><TD>Automatisch Angriffe im Allianz Chat posten.</td></tr>\
					<TR><TD></td><TD><TABLE cellpadding=0 cellspacing=0>\
					<TR><TD align=right>Nachrichten Prefix: &nbsp; </td><TD><INPUT id=pcalertPrefix type=text size=60 maxlength=120 value="'+ Options.alertConfigpdx.aPrefix +'" \></td></tr>\
					<TR><TD align=right>beim Auspähen: &nbsp; </td><TD><INPUT id=pcalertScout type=checkbox '+ (Options.alertConfigpdx.scouting?' ':'') +'/></td></tr>\
					<TR><TD align=right>bei Wildniss Angriffen: &nbsp; </td><TD><INPUT id=pcalertWild type=checkbox '+ (Options.alertConfigpdx.wilds?' ':'') +'/></td></tr>\
					<TR><TD align=right>bei Truppenstärke: &nbsp; </td><TD><INPUT id=pcalertTroops type=text size=7 value="'+ Options.alertConfigpdx.minTroops +'" \> &nbsp; &nbsp; <span id=pcalerterr></span></td></tr>\
					<TR><TD align=right>Truppen in der Botschaft Anzeigen: &nbsp; </td><TD><INPUT id=pcalertEmbassy type=checkbox '+ (Options.alertConfigpdx.embassy?' ':'') +'/></td></tr>\
					<TR><TD align=right>Eigene Truppen Anzeigen: &nbsp; </td><TD><INPUT id=pcalertMytroops type=checkbox '+ (Options.alertConfigpdx.mytroops?' ':'') +'/></td></tr>\
					<TR><TD align=right>Nahrungs Verbrauch Anzeigen: &nbsp; </td><TD><INPUT id=pcalertFood type=checkbox '+ (Options.alertConfigpdx.food?' ':'') +' /></td></tr>\
					<TR><TD align=right>Mauerdef Anzeigen: &nbsp; </td><TD><INPUT id=pcalertDefense type=checkbox '+ (Options.alertConfigpdx.defense?' ':'') +' /></td></tr>\
					<TR><TD align=right>Mein Bunker Anzeigen: &nbsp; </td><TD>'+ citySelect +'&nbsp; &nbsp;</td></tr>\
					<TR><TD align=right>Nachrichten im Allianz Chat posten: &nbsp; </td><TD><INPUT id=pcalertSendToAlly type=checkbox '+ (Options.alertConfigpdx.sendtoAlly?' ':'') +' /></td></tr>\
					<TR><TD align=right>Mich selbst anflüstern: &nbsp; </td><TD><INPUT id=pcalertSendAsWhisper type=checkbox '+ (Options.alertConfigpdx.sendasWhisper?' ':'') +' /></td></tr>\
					<TR><TD align=right>E-Mail bei Angriffen: &nbsp; </td><TD><INPUT id=pcalertSendEmail type=checkbox '+ (Options.alertConfigpdx.sendEmail?' ':'') +' /></td></tr>\
					<TR><TD align=right>E-Mail Adresse: &nbsp; </td><TD><INPUT id=pcalertEmailAddress type=text size=36 value="'+ Options.alertConfigpdx.emailAddress +'" \> &nbsp; &nbsp;</td></tr>\
					<TR><TD align=right>Mein Token: &nbsp; </td><TD><INPUT id=pcalertToken type=text size=36 value="'+ Options.alertConfigpdx.token +'" \> &nbsp; &nbsp;</td></tr>\
					</table></td></tr>\
					</table><BR><BR><HR>Hinweis: Wenn eine Box nicht klickbar ist hat KoC mal wieder ein Update gemacht!<BR><font color=#FF0000><u>WICHTIG</u></font>: Damit der Gebiete Tab Funktioniert musst du dieses Tool (<a href="http://userscripts.org/scripts/show/96308" target="_blank">KOCtools: The Script</a>) Instalieren und Laufen Lassen<BR>(Mach es aber aus solange du den Gebiete Tab nicht benutz!)<HR>KoC - Allianz Extras - Update: <a href="http://userscripts.org/scripts/show/98788" target="_blank">KoC Allianz - Extras</a> - Version: '+ Version +'</div>';
				div.innerHTML = m;

				t.togOpt ('pcalertEnable', 'pcalertEnable', Options.alertConfigpdx.aChat.setEnable);
				t.changeOpt ('pcalertPrefix', 'pcalertPrefix', Options.alertConfigpdx.aPrefix.setEnable);
				t.togOpt ('pcalertScout', 'pcalertScout', Options.alertConfigpdx.scouting.setEnable);
				t.togOpt ('pcalertWild', 'pcalertWild', Options.alertConfigpdx.wilds.setEnable);
				t.changeOpt  ('pcalertTroops', 'pcalertTroops');
				t.togOpt ('pcalertFood', 'pcalertFood', Options.alertConfigpdx.food.setEnable);	  
				t.togOpt ('pcalertEmbassy', 'pcalertEmbassy', Options.alertConfigpdx.embassy.setEnable);	  
				t.togOpt ('pcalertMytroops', 'pcalertMytroops', Options.alertConfigpdx.mytroops.setEnable);	
				t.togOpt ('pcalertDefense', 'pcalertDefense', Options.alertConfigpdx.defense.setEnable);
//				Options.alertConfigpdx.hq=document.getElementById('pcalertHQ').options[document.getElementById('pcalertHQ').selectedIndex].value;
				t.changeOpt ('pcalertHQ', 'pcalertHQ');
				t.togOpt ('pcalertSendEmail', 'pcalertSendEmail', Options.alertConfigpdx.sendEmail.setEnable);
				t.togOpt ('pcalertEmailAddress', 'pcalertEmailAddress', Options.alertConfigpdx.emailAddress.setEnable);
				t.togOpt ('pcalertToken', 'pcalertToken', Options.alertConfigpdx.token.setEnable);
				t.togOpt ('pcalertSendAsWhisper', 'pcalertSendAsWhisper', Options.alertConfigpdx.sendasWhisper.setEnable);
				t.togOpt ('pcalertSendToAlly', 'pcalertSendToAlly', Options.alertConfigpdx.sendtoAlly.setEnable);	


			} catch (e) {
				div.innerHTML = '<PRE>'+ e.name +' : '+ e.message +'</pre>';  
			}      
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.Options;
			mainPop.div.style.height = 550 + 'px';
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.Options;
			mainPop.div.style.height = 550 + 'px';
			mainPop.div.style.width = 750 + 'px';
		},

		togOpt : function (checkboxId, optionName, callEnable, callIsAvailable, callOnChange){
			var t = Tabs.Options;
			var checkbox = document.getElementById(checkboxId);
			if (Options[optionName])
				checkbox.checked = true;
			checkbox.addEventListener ('change', new eventToggle(checkboxId, optionName, callEnable).handler, false);
			function eventToggle (checkboxId, optionName, callOnChange){
				this.handler = handler;
				var optName = optionName;
				var callback = callOnChange;
				function handler(event){
					Options[optionName] = this.checked;
					saveOptions();
					if (callback != null)
						callback (this.checked);
				}
			}
		},

		changeOpt : function (valueId, optionName, callOnChange){
			var t = Tabs.Options;
			var e = document.getElementById(valueId);
			e.value = Options[optionName];
			e.addEventListener ('change', eventHandler, false);
			function eventHandler (){
				Options[optionName] = this.value;
				saveOptions();
				if (callOnChange)
					callOnChange (this.value);
			}
		},

		e_watchChanged : function (){
			GlobalOptions.pcWatchdog = document.getElementById('pcWatchEnable').checked;
			GM_setValue ('Options_??', JSON2.stringify(GlobalOptions));  
		},

		e_wideChanged : function (){
			GlobalOptions.pcWideScreen = document.getElementById('pcWideOpt').checked;
			GM_setValue ('Options_??', JSON2.stringify(GlobalOptions));  
		},
}
/****************************  Website Tab   ******************************/
Tabs.board = {
		tabOrder : 50,                    // order to place tab in top bar
		tabLabel : 'Website',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		forumlink :  'http://koc.god-like.org',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.board;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe src="'+ t.forumlink +'" width="100%" height="500" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.forumlink +'">KoC Allianz - Extras</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.board;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.board;
			mainPop.div.style.width = 750 + 'px';
		},
}
/****************************  Teamspeak Tab   ******************************/
Tabs.teamspeak = {
		tabOrder : 53,                    // order to place tab in top bar
		tabLabel : 'TS³',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		forumlink :  'http://ts3.god-like.info/tsviewpub.php?skey=0&port=31337&showicons=right&bgcolor=none&fontcolor=000000',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.teamspeak;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe allowtransparency="true" src="'+ t.forumlink +'" width="100%" height="500" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.forumlink +'">god-like.org - Teamspeak Server</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.teamspeak;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.teamspeak;
			mainPop.div.style.width = 750 + 'px';
		},
}

/****************************  Map Tab   ******************************/
Tabs.map = {
		tabOrder : 40,                    // order to place tab in top bar
		tabLabel : 'Gebiete',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		forumlink :  'http://koctools.com/index.php?pageid=servers',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.map;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe src="'+ t.forumlink +'" width="100%" height="500" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.forumlink +'">KoC Server Gebiete</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.map;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.map;
			mainPop.div.style.width = 750 + 'px';
		},
}
/****************************  Mappa tab   ******************************/
Tabs.Mappa = {
		tabOrder : 42,                    // order to place tab in top bar
		tabLabel : 'Karte',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		forumlink :  'http://koc.dunno.com/index.sjs?f=KocMapViewer&server_id=179',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.Mappa;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe src="'+ t.forumlink +'" width="100%" height="550" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.forumlink +'">Truppen Spostamenti</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.Mappa;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.Mappa;
			mainPop.div.style.width = 750 + 'px';
		},
}

/****************************  Script Tab   ******************************/
Tabs.script = {
		tabOrder : 70,                    // order to place tab in top bar
		tabLabel : 'Scripts',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		forumlink :  'http://userscripts.org/users/297645/scripts',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.script;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe src="'+ t.forumlink +'" width="100%" height="500" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.forumlink +'">My Userscripts.org</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.script;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.script;
			mainPop.div.style.width = 750 + 'px';
		},
}
/****************************  Rechner Tab   ******************************/
Tabs.rechner = {
		tabOrder : 30,                    // order to place tab in top bar
		tabLabel : 'Rechner',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		forumlink :  'http://koc.god-like.info/rechner.html',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.rechner;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe src="'+ t.forumlink +'" width="100%" height="500" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.forumlink +'">Truppen Rechner</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.rechner;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.rechner;
			mainPop.div.style.width = 750 + 'px';
		},
}

/****************************  Wikia Tab   ******************************/
Tabs.wikia = {
		tabOrder : 60,                    // order to place tab in top bar
		tabLabel : 'Wiki',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		forumlink :  'http://www.kingdoms-of-camelot.de',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.wikia;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe src="'+ t.forumlink +'" width="100%" height="500" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.forumlink +'">Kingdom of Camelot - Wiki - Deutsch</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.wikia;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.wikia;
			mainPop.div.style.width = 750 + 'px';
		},
}


/****************************  External Chat Tab   ******************************/
Tabs.extChat = {
		tabOrder : 50,                    // order to place tab in top bar
		tabLabel : 'Chat',            // label to show in main window tabs
		myDiv : null,
		timer : null,
		chatlink :  'http://webchat.quakenet.org/?channels=PDX&uio=Mj10cnVlJjQ9dHJ1ZSY5PXRydWUmMTA9dHJ1ZSYxMT0zNDkmMTI9dHJ1ZQ57',  

		init : function (div){    // called once, upon script startup
			var t = Tabs.extChat;
			t.myDiv = div;
			div.innerHTML = '<CENTER><iframe src="'+ t.chatlink +'" width="100%" height="500" name="board_in_a_box" <p>Ihr Browser kann leider keine eingebetteten Frames anzeigen: Sie k&ouml;nnen die eingebettete Seite &uuml;ber den folgenden Verweis aufrufen: <a href="'+ t.chatlink +'">Allianz Chat</a></p> </iframe> <BR></center>';
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.extChat;  	  
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.extChat;  
			mainPop.div.style.width = 750 + 'px';

		},
}

/*********************************** Log Tab ***********************************/
Tabs.ActionLog = {
		tabOrder: 80,
		tabLabel : 'Log',
		myDiv : null,
		logTab : null,
		maxEntries: 300,
		last50 : [],
		state : null,

		init : function (div){
			var t = Tabs.ActionLog;
			t.myDiv = div;
			t.myDiv.innerHTML = '<DIV class=pcstat>ACTION LOG</div><DIV style="height:500px; max-height:500px; overflow-y:auto">\
				<TABLE cellpadding=0 cellspacing=0 id=pcactionlog class=pcTabLined><TR><TD></td><TD width=95%></td></table></div>';
			t.logTab = document.getElementById('pcactionlog');  
			t.state = 1;
			var a = JSON2.parse(GM_getValue ('log_'+getServerId(), '[]'));
			if (matTypeof(a) == 'array'){
				t.last50 = a;
				for (var i=0; i<t.last50.length; i++)
					t._addTab (t.last50[i].msg, t.last50[i].ts);
			}
			window.addEventListener('unload', t.onUnload, false);
		},

		hide : function (){         // called whenever the main window is hidden, or another tab is selected
			var t = Tabs.ActionLog;
			mainPop.div.style.width = 750 + 'px';
		},

		show : function (){         // called whenever this tab is shown
			var t = Tabs.ActionLog;
			mainPop.div.style.width = 750 + 'px';
		},


		onUnload : function (){
			var t = Tabs.ActionLog;
			GM_setValue ('log_'+getServerId(), JSON2.stringify(t.last50));
		},

		_addTab : function (msg, ts){
			var t = Tabs.ActionLog;
			if (t.state != 1)
				return;
			if (t.logTab.rows.length >= t.maxEntries)
				t.logTab.deleteRow(t.maxEntries-1);
			var row = t.logTab.insertRow(0);
			row.vAlign = 'top';
			row.insertCell(0).innerHTML = ts;
			row.insertCell(1).innerHTML = msg;
		}, 

		log : function (msg){
			var t = Tabs.ActionLog;
			var ts = new Date().toTimeString().substring (0,8);
			t._addTab (msg, ts);
			while (t.last50.length >= 50)
				t.last50.shift();
			t.last50.push ({msg:msg, ts:ts});
		},
}

function actionLog (msg){
	if (!Tabs.ActionLog.tabDisabled)
		Tabs.ActionLog.log (msg);  
}

/************************ Tower Alerts ************************/
var TowerAlerts = {
		generateIncomingFunc : null,
		fixTargetEnabled : false,
		towerMarches : {},    // track all marches that have been posted to alliance chat

		init : function (){
			var t = TowerAlerts; 
			var s = GM_getValue ('towerMarches_'+getServerId());
			if (s != null)
				t.towerMarches = JSON2.parse (s);

			t.generateIncomingFunc = new CalterUwFunc ('attack_generateincoming', [[/.*} else {\s*e = true;\s*}/im, '} else { e = ptGenerateIncoming_hook(); }']]);
			unsafeWindow.ptGenerateIncoming_hook = t.generateIncoming_hook;
		},

		postToChatOptions : {aChat:false},
		secondTimer : null,

		setPostToChatOptions : function (obj){
			var t = TowerAlerts;
			t.postToChatOptions = obj;
			clearTimeout(t.secondTimer);
			if (obj.aChat)
				t.e_eachSecond();
			//DD two lines deleted
		},

		addTowerMarch : function (m){
			var t = TowerAlerts;
			var now = unixTime();
			for (k in t.towerMarches){
				if (t.towerMarches[k].arrival < now)
					delete t.towerMarches[k];
			}
			t.towerMarches['m'+m.mid] = {added:now, arrival:parseIntNan(m.arrivalTime) };
			GM_setValue ('towerMarches_'+getServerId(), JSON2.stringify(t.towerMarches) );
		},

		getTowerMarch : function (mid){ // ID only (no 'm')
			var t = TowerAlerts;
			return t.towerMarches['m'+mid];
		},


		e_eachSecond : function (){ // check for incoming marches
			var t = TowerAlerts;
			var now = unixTime();
			if (matTypeof(Seed.queue_atkinc) != 'array'){
				for (var k in Seed.queue_atkinc){
					var m = Seed.queue_atkinc[k];
					if ((m.marchType==3 || m.marchType==4) && parseIntNan(m.arrivalTime)>now && t.getTowerMarch(m.mid)==null){
						t.addTowerMarch (m);
						if(Options.alertConfigpdx.aChat)
							t.postToChat(m, false);
					}
				}
			}
			t.secondTimer = setTimeout (t.e_eachSecond, 2000);
		},

		postToChat: function(m, force){
			var t = TowerAlerts;
			var troopsnames = ['Versorger', 'Miliz', 'Späher', 'Lanzen', 'Schwerter', 'Bogis', 'Kav', 'SKav', 'Wagen', 'Ballis', 'Ram', 'Katapulte'];
			var tot = [];

			for (i = 0; i < 13; i++) 
				tot[i] = 0;

			if (DEBUG_TRACE) {
				logit("checkTower(): INCOMING bei " + unixTime() + ": \n" + inspect(m, 8, 1));
			}	
			if (m.marchType == null) {// bogus march (returning scouts)
				if (DEBUG_TRACE) {
					logit("checkTower(): m.marchType == null");
				}	
				return;
			}	


			if (m.marchType == 3) {
				if (!Options.alertConfigpdx.scouting && !force) 
					return;
				atkType = 'Spähen';
				if (DEBUG_TRACE) {
					logit("checkTower(): scout");
				}	
			}
			else {
				if (m.marchType == 4) {
					atkType = 'Angriff';
					if (DEBUG_TRACE) {
						logit("checkTower(): attack");
					}
				}
				else {
					if (DEBUG_TRACE) {
						logit("checkTower(): unkown march typ");
					}
					return;
				}
			}

			if (DEBUG_TRACE) {
				logit("checkTower(): after typ");
			}	
			var target, atkType, who;
			var city = Cities.byID[m.toCityId];
			if (city.tileId == m.toTileId) 
				target = ' ' + city.name + ' (' + city.x + ',' + city.y+') ';
			else {
				if (!Options.alertConfigpdx.wilds && !force) 
					return;
				target = 'Wildniss';
				for (k in Seed.wilderness['city' + m.toCityId]) {
					if (Seed.wilderness['city' + m.toCityId][k].tileId == m.toTileId) {
						target += '(' + Seed.wilderness['city' + m.toCityId][k].xCoord + ',' + Seed.wilderness['city' + m.toCityId][k].yCoord+') ';
						break;
					}
				}
			}


			var attackermight = '';
			var allianceId = '';

			if (Seed.players['u' + m.pid]) {
				who = Seed.players['u' + m.pid].n;
				attackermight = parseInt(Seed.players['u' + m.pid].m);
				allianceId = Seed.players['u' + m.pid].a;
			} else {
				if (m.players && m.players['u' + m.pid]) {
					who = m.players['u' + m.pid].n;
					attackermight = parseInt(m.players['u' + m.pid].m);
					allianceId = m.players['u' + m.pid].a;
				} else {
					who = 'Unknown';
				}
			}

			if (m.fromXCoord) 
				who += ' bei ' + m.fromXCoord + ',' + m.fromYCoord;

			var diplomacy = getDiplomacy(allianceId);


			var arrivingDatetime = new Date();
			arrivingDatetime.setTime(m.arrivalTime * 1000);

			if (DEBUG_TRACE) {
				logit("checkTower(): Before Messagecreate");
			}
			/** ** message ** */
			var msg = '';
			var subject = '';

			if (!force) 
				msg = Options.alertConfigpdx.aPrefix + ' ';

			msg += 'Mein Bunker: ' + Options.alertConfigpdx.hq + ' = ';
			msg += 'Marsch Typ: ' + atkType + ' = ';
			msg += 'Ziel: ' + target + ' = ';
			msg += 'Angreifer: ' + who + ' (' + addCommas(attackermight) + ', ' + diplomacy + ') ';

			msg +='= Truppenstärke vom Angreifer = ';
			var totTroops = 0;
			for (k in m.unts) {
				var uid = parseInt(k.substr(1));
				msg += m.unts[k] + ' ' + unsafeWindow.unitcost['unt' + uid][0] + ', ';
				totTroops += m.unts[k];
			}

			if ((totTroops < Options.alertConfigpdx.minTroops) && !force) 
				return;

			msg = msg.slice(0, -2);
			msg += ' (Ankunft in ' + unsafeWindow.timestr(parseInt(m.arrivalTime - unixTime())) + ') ' ;

			/** ** Embassy** **/
			if (city.tileId == m.toTileId) {
				var emb = getCityBuilding(m.toCityId, 8);
				if (emb.count > 0) {
					var availSlots = emb.maxLevel;
					for (k in Seed.queue_atkinc) {
						if (Seed.queue_atkinc[k].marchType == 2 && Cities.byID[Seed.queue_atkinc[k].fromCityId] == null) {
							--availSlots;
						}
					}
					msg += '= Botschaft (' + availSlots + '/' + emb.maxLevel + ')Slots =';

					if (DEBUG_TRACE) {
						logit("checkTower(): Mesage Part1:"+msg);
					}
					var msgpart1 = msg;
					msg ='';
					if (DEBUG_TRACE) {
						logit("checkTower(): in Embassy: "+Options.alertConfigpdx.embassy);
					}

					if (Options.alertConfigpdx.embassy) {
						var rownum = 0;
						enc = {};
						numSlots = 0;

						if (matTypeof(Seed.queue_atkinc) != 'array') {
							for (k in Seed.queue_atkinc) {
								march = Seed.queue_atkinc[k];
								if (march.marchType == 2) {
									++numSlots;
									city = march.toCityId;
									from = march.fromPlayerId;
									if (!enc[city]) 
										enc[city] = {};
									if (!enc[city][from]) 
										enc[city][from] = [];
									s = [];
									s[0] = parseInt(march.knightCombat);
									for (i = 1; i < 13; i++) {
										if (Options.encRemaining) 
											s[i] = parseInt(march['unit' + i + 'Return']);
										else 
											s[i] = parseInt(march['unit' + i + 'Count']);
									}
									enc[city][from].push(s);
								}
							}
						}


						dest = m.toCityId;
						var embassyOccupied = false;
						if (enc[dest]) {
							embassyOccupied = true;
							msg += ' Truppen in der Botschaft: ';
							for (p in enc[dest]) {
								try {
									if (Seed.players['u' +p]) {
										player = Seed.players['u' + p].n;
									} else {
										if (m.players && m.players['u' + p]) {
											player = m.players['u' + p].n;
										} else {
											player = 'Unknown';
										}
									}

								} 
								catch (err) {
									player = '???';
								}
								for (j = 0; j < enc[dest][p].length; j++) {
									knight = '';
									if (enc[dest][p][j][0] > 0) 
										knight = ' (' + enc[dest][p][j][0] + ')';
									var slot = j + 1;
									msg += '[Slot' + slot + ': ' + player + knight + ' - ';
									for (i = 1; i < 13; i++) {
										num = enc[dest][p][j][i];
										if (num > 0) 
											msg += '' + addCommas(num) + ' ' + troopsnames[i - 1] + ', ';
										tot[i] += num;
									}
									msg += ']';
								}//for (j=0; j<enc[dest][p].length; j++)
							}//for (p in enc[dest])
						}//if (enc[dest])

					}// if (Options.alertConfigpdx.embassy)
				}//if (emb.count > 0)
			}// if ( city.tileId == m.toTileId )
			/** ****Embassy End *** */

			if (DEBUG_TRACE) {
				logit("checkTower(): Mesage Part2:"+msg);
			}
			var msgpart2 = msg;
			msg ='';

			/** ** My Troops ** */
			if(Options.alertConfigpdx.mytroops) t.postToChat(m); {
				msg += ' = Truppen in der Stadt: ';
				cityID = 'city' + m.toCityId;

				for (r = 1; r < 13; r++) {
					num = parseInt(Seed.units[cityID]['unt' + r]);
					tot[r] += num;
					if (num > 0) 
						msg += '' + addCommas(num) + ' ' + troopsnames[r - 1] + ', ';
				}
				msg += ' ';
			}
			if (DEBUG_TRACE) {
				logit("checkTower(): Mesage Part3:"+msg);
			}
			var msgpart3 = msg;
			msg ='';  
			/** ** My Troops:End ** */

			/** ** Total Troops ** */
			if (embassyOccupied) {
				msg += ' = Truppen Insgesammt: ';
				for (r = 1; r < 13; r++) {
					num = parseInt(tot[r]);
					if (num > 0) 
						msg += '' + addCommas(num) + ' ' + troopsnames[r - 1] + ', ';
				}
				msg += ' = ';
			}
			var msgpart4 = msg;
			if (DEBUG_TRACE) {
				logit("checkTower(): Mesage Part4:"+msg);
			}
			msg='';
			/** ** Total Troops:End ** */

			// Food usage
			if (Options.alertConfigpdx.food) {
				var rp = getResourceProduction (m.toCityId);
				var usage = parseInt(Seed.resources["city" + m.toCityId]['rec1'][3]);
				var foodIncome = rp[1] - usage;

				var food = parseInt(Seed.resources['city'+ m.toCityId]['rec'+1][0] / 3600);
				var timeLeft = parseInt(Seed.resources["city" + m.toCityId]['rec1'][0]) / 3600 / (0-foodIncome) * 3600;
				var timeLeftShort  = timestrShort(timeLeft);

				msg += ' Nahrung:';
				msg += ' '+addCommas(food)+ ' (' +addCommas(foodIncome)+') reicht noch für: '+timeLeftShort+' = ';
			}
			var msgpart5 =msg;
			if (DEBUG_TRACE) {
				logit("checkTower(): Mesage Part5:"+msg);
			}
			msg ='';
			// My Defense
			if (Options.alertConfigpdx.defense) {
				msg += ' Meine Mauerdef: ';
				var fortifications = [];
				var fortificationsVal = [];

				fortifications['53'] = 'Fest montierte Armbrüste';
				fortifications['55'] = 'Abwehr Trebuchet';
				fortifications['60'] = 'Fallen';
				fortifications['61'] = 'Krähenfüße';
				fortifications['62'] = 'Eiserne Erdspieße';

				for (id in fortifications) {
					if (IsNumeric(id)) {
						var fortiName = fortifications[id];
						var fortiVal = parseInt(Seed.fortifications['city' + m.toCityId]['fort'+id]);
						msg += fortiVal +' '+fortiName+', ';  
					} 
				}
			}
			var msgpart6 =msg;
			msg ='';

			//Spares
			var msgpart7 =msg;
			msg ='';
			var msgpart8 =msg;
			msg ='';

			//build message together;
			msg = msgpart1 + msgpart2 + msgpart3 + msgpart4 + msgpart5 + msgpart6 + msgpart7 + msgpart8;
			if (DEBUG_TRACE) {
				logit("checkTower(): full Mesage:"+msg);
			}

			if (Options.alertConfigpdx.sendasWhisper) {
				if (DEBUG_TRACE) {
					logit("checkTower(): Options.alertConfigpdx.sendasWhisper:"+"/" + Seed.player.name + ' ' + msg);
				}
				sendChat("/" + Seed.player.name + ' ' + msg); // Whisper to myself
			}

			if (Options.alertConfigpdx.sendtoAlly) {
				sendChat("/a " + msg); // Alliance chat
			}

			//EMAIL		
			if (Options.alertConfigpdx.sendEmail) {
				var subject = target + ' wird ' + atkType + ' von ' + who + ' um '+ arrivingDatetime;
				var token = Options.alertConfigpdx.token;
				var email = Options.alertConfigpdx.emailAddress;
				var error = false;

				if (token=="") { 
					error = true;
					if (DEBUG_TRACE) logit("Allianz Extras: error: kein token... ");
				}
				if (email=="") { 
					error = true;
					if (DEBUG_TRACE) logit("Allianz Extras: error: keine e-mail ");
				}
				if (subject=="") { 
					error = true;
					if (DEBUG_TRACE) logit("Allianz Extras: error: kein betreff");
				}
				if (msgpart1=="") { 
					error = true;
					if (DEBUG_TRACE) logit("Allianz Extras: error: kein msgpart1 ");
				}

				if (!error) {
					sendEmail(Seed.player.name, token, email, subject, msgpart1, msgpart2, msgpart3, msgpart4, msgpart5, msgpart6, msgpart7, msgpart8);
					if (DEBUG_TRACE) logit("Allianz Extras: Email wurde verschickt!");
					var msgnotify = "Allianz Extras: Email wurde verschickt!";
					sendChat("/" + Seed.player.name + ' ' + msgnotify); // Whisper to myself
				}

			}

		}, // postToChat
} //TowerAlerts




/**
 * SendEmail
 */
function sendEmail (username, token, email, subject, msgpart1, msgpart2, msgpart3, msgpart4, msgpart5, msgpart6, msgpart7, msgpart8){  
	if (DEBUG_TRACE) logit ("sendEmail(): entry ");

	var params = 'user:'+username;		
	var query = "?user="+username+"&token="+token+"&email="+email+"&subject="+subject+"&msg1="+msgpart1+"&msg2="+msgpart2+"&msg3="+msgpart3+"&msg4="+msgpart4+"&msg5="+msgpart5+"&msg6="+msgpart6+"&msg7="+msgpart7+"&msg8="+msgpart8;
	//unsafeWindow.g_ajaxsuffix

	new MyAjaxRequest( "http://phoenixmailer.co.de/mailer.php" + query, {
		method: "POST",
		parameters: params,
		onSuccess: function (rslt) {
			if (DEBUG_TRACE) logit ("sendEmail(): success ");
		},
		onFailure: function () {
			if (DEBUG_TRACE) logit ("sendEmail() :failure");
		},
	});
} //sendMail

function getResourceProduction (cityId){
	var ret = [0,0,0,0,0];
	var now = unixTime ();

	var wilds = [0, 0, 0, 0, 0];
	var w = Seed.wilderness["city" + cityId];
	for (var k in w){
		var type = parseInt(w[k].tileType);
		if (type==10 || type==11)
			wilds[1] += parseInt(w[k].tileLevel);
		else 
			wilds[type/10] += parseInt(w[k].tileLevel);
	}  

	knight = 0;       
	var s = Seed.knights["city" + cityId];
	if (s) {
		s = s["knt" + Seed.leaders["city" + cityId].resourcefulnessKnightId];
		if (s){
			var knight = parseInt(s.resourcefulness);
			if (s.resourcefulnessBoostExpireUnixtime > now)
				knight *= 1.25;
		}
	}
	var workerFactor = 1;
	var c = parseInt(Seed.citystats["city" + cityId]["pop"][0]);  // Current
	// population
	var w = parseInt(Seed.citystats["city" + cityId]["pop"][3]);  // Labor force
	if (w > c)
		workerFactor = c / w;

	for (var i=1; i<5; i++){
		var usage = Seed.resources["city" + cityId]["rec" + i];
		var items = 0;
		if (parseInt(Seed.playerEffects["r" + i + "BstExp"]) > now) {
			items = 0.25;
		}
		var tech = Seed.tech["tch" + i];
		ret[i] = parseInt((usage[2] * (1 + tech/10 + knight/100 + items + 0.05 * wilds[i]) * workerFactor + 100));
	}
	return ret;  
}

function IsNumeric(input)
{
	return (input - 0) == input && input.length > 0;
}

/********** facebook watchdog: runs only in 'http://apps.facebook.com/kingdomsofcamelot/*' instance!  ******/
function facebookWatchdog (){
	var INTERVAL = 50000; // wait 50 seconds minute before checking DOM
	if (!GlobalOptions.pcWatchdog)
		return;
	setTimeout (watchdog, INTERVAL);

//	TODO: actionLog ?  
	function watchdog (){
		logit ("--- Facebook WATCHDOG");    
		try {
//			if (document.getElementById('app_content_130402594779').firstChild.firstChild.childNodes[1].firstChild.tagName!='IFRAME'){
			if (document.getElementById('app_content_130402594779') == null){
				logit ("KOC NOT FOUND!"); 
				KOCnotFound(5*60);
			} 
		} catch (e){
			logit ("KOC NOT FOUND!"); 
			KOCnotFound(5*60);
		} 
	}
}


function kocWatchdog (){
	var INTERVAL = 30000; // wait 30 seconds before checking DOM
	if (!GlobalOptions.pcWatchdog)
		return;
	setTimeout (watchdog, INTERVAL);
	function watchdog (){
		logit ("--- KOC WATCHDOG");    
		if (document.getElementById('mod_maparea')==null){
			actionLog ("Allianz Extras: KoC ist nicht geladen");
			KOCnotFound(3*60);
		}     
	}
}


function KOCnotFound(secs){
	var div;
	var countdownTimer = null;
	var endSecs = (new Date().getTime()/1000) + secs;

	div = document.createElement('div');
	div.innerHTML = '<DIV style="font-size:18px; background-color:#a00; color:#fff"><CENTER><BR>KOC Power Bot has detected that KOC is not loaded<BR>\
		Refreshing in <SPAN id=pcwdsecs></span><BR><INPUT id=pcwdcan type=submit value="Cancel Refresh"><BR><BR></div>';
	document.body.insertBefore (div, document.body.firstChild);
	document.getElementById('pcwdcan').addEventListener('click', cancel, false);
	countdown();

	function countdown (){
		var secsLeft = endSecs - (new Date().getTime()/1000);
		document.getElementById('pcwdsecs').innerHTML = timestr(secsLeft);
		if (secsLeft < 0)
			reloadKOC();
		countdownTimer = setTimeout (countdown, 1000);
	}
	function cancel (){
		clearTimeout (countdownTimer);
		document.body.removeChild (div);
	}
}



var WideScreen = {
		chatIsRight : false,
		useWideMap : false,

		init : function (){
			if (GlobalOptions.pcWideScreen){
				GM_addStyle ('.modalCurtain {width:750px !important} .mod_comm_mmb{z-index:0 !important}');  
				try {
					document.getElementById('progressBar').parentNode.removeChild(document.getElementById('progressBar'));
				} catch (e) {
				}
			}
		},

		setChatOnRight : function (tf){
			t = WideScreen;
			if (tf == t.chatIsRight || !GlobalOptions.pcWideScreen)
				return;
			if (tf){
				var chat = document.getElementById('kocmain_bottom').childNodes[1];
				if (!chat || chat.className!='mod_comm')
					setTimeout (function (){t.setChatOnRight(tf)}, 1000); 
				chat.style.top = '-624px';
				chat.style.left = '760px';
				chat.style.height = '720px';
				chat.style.background = 'url("'+ CHAT_BG_IMAGE +'")';
				document.getElementById('mod_comm_list1').style.height = '580px';
				document.getElementById('mod_comm_list2').style.height = '580px';
			} else {
				var chat = document.getElementById('kocmain_bottom').childNodes[1];
				chat.style.top = '0px';
				chat.style.left = '0px';
				chat.style.height = '';
				chat.style.background = '';
				document.getElementById('mod_comm_list1').style.height = '287px';
				document.getElementById('mod_comm_list2').style.height = '287px';
			}
			t.chatIsRight = tf;
		},
		useWideMap : function (tf) {
			t = WideScreen;
			if (tf == t.useWideMap || !GlobalOptions.pcWideScreen)
				return;
			if (tf){
				document.getElementById('mapwindow').style.height = "436px";
				document.getElementById('mapwindow').style.width = "1220px";
				document.getElementById('mapwindow').style.zIndex = "50";
			} else {
				document.getElementById('mapwindow').style.height = "439px";
				document.getElementById('mapwindow').style.width = "760px";
				document.getElementById('mapwindow').style.zIndex = "";
			}
		},
}



/*******************   KOC Map interface ****************/
//0:bog, 10:grassland, 11:lake, 20:woods, 30:hills, 40:mountain, 50:plain, 51:city / barb, 53:misted city
function CMapAjax (){
	this.normalize = normalize;  
	this.request = request;

	function request (left, top, width, notify){    
		var left = parseInt(left / 5) * 5;
		var top = parseInt(top / 5) * 5;
		var width = parseInt((width+4) / 5) * 5;

		var blockString = generateBlockList(left, top, width);
		var params = unsafeWindow.Object.clone(unsafeWindow.g_ajaxparams);
		params.blocks = blockString;
		new MyAjaxRequest(unsafeWindow.g_ajaxpath + "ajax/fetchMapTiles.php" + unsafeWindow.g_ajaxsuffix, {
			method: "post",
			parameters: params,
			onSuccess: function (rslt) {
				notify(left, top, width, rslt);
			},
			onFailure: function (rslt) {
				notify(left, top, width, rslt);
			}
		});
		function generateBlockList (left, top, width) {
			var width5 = parseInt(width / 5);
			var bl = [];
			for (x=0; x<width5; x++){
				var xx = left + (x*5);
				if (xx > 745)
					xx -= 750;
				for (y=0; y<width5; y++){
					var yy = top + (y*5);
					if (yy > 745)
						yy -= 750;
					bl.push ('bl_'+ xx +'_bt_'+ yy);
				}
			}
			return bl.join(",");
		}
	}

	function normalize  (x){
		if ( x >= 750)
			x -= 750;
		else if (x < 0)
			x += 750;
		return parseInt (x/5) * 5;
	}
}


var tabManager = {
		tabList : {},           // {name, obj, div}
		currentTab : null,

		init : function (mainDiv){
			var t = tabManager;
			var sorter = [];
			for (k in Tabs){
				if (!Tabs[k].tabDisabled){  
					t.tabList[k] = {};
					t.tabList[k].name = k;
					t.tabList[k].obj = Tabs[k];
					if (Tabs[k].tabLabel != null)
						t.tabList[k].label = Tabs[k].tabLabel;
					else
						t.tabList[k].label = k;
					if (Tabs[k].tabOrder != null)
						sorter.push([Tabs[k].tabOrder, t.tabList[k]]);
					else
						sorter.push([1000, t.tabList[k]]);
					t.tabList[k].div = document.createElement('div');
				}
			}

			sorter.sort (function (a,b){return a[0]-b[0]});
			var m = '<TABLE cellspacing=0 class=pcMainTab><TR>';
			for (var i=0; i<sorter.length; i++)
				m += '<TD class=spacer></td><TD class=notSel id=pctc'+ sorter[i][1].name +' ><A><SPAN>'+ sorter[i][1].label +'</span></a></td>';
			m += '<TD class=spacer width=90% align=right>&nbsp;</td></tr></table>';
			mainPop.getTopDiv().innerHTML = m;

			for (k in t.tabList) {
				if (t.currentTab == null)
					t.currentTab = t.tabList[k];
				document.getElementById('pctc'+ k).addEventListener('click', this.e_clickedTab, false);
				var div = t.tabList[k].div; 
				div.style.display = 'none';
				div.style.height = '100%';
				mainDiv.appendChild(div);
				try {
					t.tabList[k].obj.init(div);
				} catch (e){
					div.innerHTML = "INIT ERROR: "+ e;
				}
			}

			t.setTabStyle (document.getElementById ('pctc'+ t.currentTab.name), true);
			t.currentTab.div.style.display = 'block';
		},

		hideTab : function (){
			var t = tabManager;
			t.currentTab.obj.hide();
		},

		showTab : function (){
			var t = tabManager;
			t.currentTab.obj.show();
		},

		setTabStyle : function (e, selected){
			if (selected){
				e.className = 'sel';
			} else {
				e.className = 'notSel';
			}
		},

		e_clickedTab : function (e){
			var t = tabManager;
			newTab = t.tabList[e.target.parentNode.parentNode.id.substring(4)];
			if (t.currentTab.name != newTab.name){
				t.setTabStyle (document.getElementById ('pctc'+ t.currentTab.name), false);
				t.setTabStyle (document.getElementById ('pctc'+ newTab.name), true);
				t.currentTab.obj.hide ();
				t.currentTab.div.style.display = 'none';

				t.currentTab = newTab;
				newTab.div.style.display = 'block';
			}
			newTab.obj.show();
		},
}


function onUnload (){
	Options.pcWinPos = mainPop.getLocation();
	saveOptions();
}

function mouseMainTab (me){   // right-click on main button resets window location
	if (me.button == 2){
		var c = getClientCoords (document.getElementById('main_engagement_tabs'));
		mainPop.setLocation ({x: c.x+4, y: c.y+c.height});
	}
}

function eventHideShow (){
	if (mainPop.toggleHide(mainPop)){
		tabManager.showTab();
		Options.pcWinIsOpen = true;
	} else {
		tabManager.hideTab();
		Options.pcWinIsOpen = false;
	}
	saveOptions();
}

function hideMe (){
	mainPop.show (false);
	tabManager.hideTab();
	Options.pcWinIsOpen = false;
	saveOptions();
}

function showMe (){
	mainPop.show (true);
	tabManager.showTab();
	Options.pcWinIsOpen = true;
	saveOptions();
}

function addMyFunction (func){      // add function to run in our own scope
	unsafeWindow[func.name] = func;
}

function addUwFunction (func){      // add function to run in unsafeWindow's scope
	var scr = document.createElement('script');
	scr.innerHTML = func.toString();
	document.body.appendChild(scr);
}

function alterUwFunction (funcName, frArray){
	try {
		funcText = unsafeWindow[funcName].toString();
		rt = funcText.replace ('function '+funcName, 'function');
		for (i=0; i<frArray.length; i++){
			x = rt.replace(frArray[i][0], frArray[i][1]);
			if (x == rt)
				return false;
			rt = x;
		}
		js = funcName +' = '+ rt;
		var scr=document.createElement('script');
		scr.innerHTML=js;
		document.body.appendChild(scr);
		return true;
	} catch (err) {
		return false;
	}
}

function officerId2String (oid){
	if (oid==null)
		return '';
	else if (oid==3)
		return 'Offizer';
	else if (oid==2)
		return 'Vize Kanzler';
	else if (oid==1)
		return 'Kanzler';
	return '';
}

var knightRoles = {
		Foreman : 'politics',
		Marshall : 'combat',
		Alchemystic : 'intelligence',
		Steward : 'resourcefulness',
};

function officerId2String (oid){
	if (oid==null)
		return '';
	else if (oid==3)
		return 'Offizer';
	else if (oid==2)
		return 'Vize Kanzler';
	else if (oid==1)
		return 'Kanzler';
	return '';
}

var fortNamesShort = {
		53: "Fest montierte Armbrüste",
		55: "Abwehr Trebuchet",
		60: "Fallen",
		61: "Krähenfüße",
		62: "Eiserne Erdspieße",
}

//onClick (city{name, id, x, y}, x, y)   city may be null!
function CdispCityPicker (id, span, dispName, notify, selbut){
	function CcityButHandler (t){
		var that = t;
		this.clickedCityBut = clickedCityBut;
		function clickedCityBut (e){
			if (that.selected != null)
				that.selected.className = "castleBut castleButNon";
			that.city = Cities.cities[e.target.id.substr(that.prefixLen)];
			if (that.dispName)
				document.getElementById(that.id+'cname').innerHTML = that.city.name;
			e.target.className = "castleBut castleButSel";
			that.selected = e.target;
			if (that.coordBoxX){
				that.coordBoxX.value = that.city.x;
				that.coordBoxY.value = that.city.y;
				that.coordBoxX.style.backgroundColor = '#ffffff';
				that.coordBoxY.style.backgroundColor = '#ffffff';
			}
			if (that.notify != null)
				that.notify(that.city, that.city.x, that.city.y);
		}
	}

	function selectBut (idx){
		document.getElementById(this.id+'_'+idx).click();
	}

	function bindToXYboxes (eX, eY){
		function CboxHandler (t){
			var that = t;
			this.eventChange = eventChange;
			if (that.city){
				eX.value = that.city.x;
				eY.value = that.city.y;
			}
			function eventChange (){
				var x = parseInt(that.coordBoxX.value, 10);
				var y = parseInt(that.coordBoxY.value, 10);
				if (isNaN(x) || x<0 || x>750){
					that.coordBoxX.style.backgroundColor = '#ff8888';
					return;
				}
				if (isNaN(y) || y<0 || y>750){
					that.coordBoxY.style.backgroundColor = '#ff8888';
					return;
				}
				that.coordBoxX.style.backgroundColor = '#ffffff';
				that.coordBoxY.style.backgroundColor = '#ffffff';
				if (that.notify != null)
					that.notify (null, x, y);
			}
		}
		this.coordBoxX = eX;
		this.coordBoxY = eY;
		var bh = new CboxHandler(this);
		eX.size=2;
		eX.maxLength=3;
		eY.size=2;
		eY.maxLength=3;
		eX.addEventListener('change', bh.eventChange, false);
		eY.addEventListener('change', bh.eventChange, false);
	}

	this.selectBut = selectBut;
	this.bindToXYboxes = bindToXYboxes;
	this.coordBoxX = null;
	this.coordBoxY = null;
	this.id = id;
	this.dispName = dispName;
	this.prefixLen = id.length+1;
	this.notify = notify;
	this.selected = null;
	this.city = null;
	var m = '';
	for (var i=0; i<Cities.cities.length; i++)
		m += '<INPUT class="castleBut castleButNon" id="'+ id +'_'+ i +'" value="'+ (i+1) +'" type=submit \>';
	if (dispName)
		m += ' &nbsp; <SPAN style="display:inline-block; width:85px; font-weight:bold;" id='+ id +'cname' +'></span>';
	span.innerHTML = m;
	var handler = new CcityButHandler(this);
	for (var i=0; i<Cities.cities.length; i++)
		document.getElementById (id+'_'+i).addEventListener('click', handler.clickedCityBut, false);
	if (selbut != null)
		this.selectBut(selbut);
};

function setCities(){
	Cities.numCities = Seed.cities.length;
	Cities.cities = [];
	Cities.byID = {};
	for (i=0; i<Cities.numCities; i++){
		city = {};
		city.idx = i;
		city.id = parseInt(Seed.cities[i][0]);
		city.name = Seed.cities[i][1];
		city.x = parseInt(Seed.cities[i][2]);
		city.y = parseInt(Seed.cities[i][3]);
		city.tileId = parseInt(Seed.cities[i][5]);
		Cities.cities[i] = city;
		Cities.byID[Seed.cities[i][0]] = city;
	}
}


function dialogRetry (errMsg, seconds, onRetry, onCancel){
	seconds = parseInt(seconds);
	var pop = new CPopup ('pcretry', 0, 0, 400,200, true);
	pop.centerMe(mainPop.getMainDiv());
	pop.getTopDiv().innerHTML = '<CENTER>KoC Allianz - Extras</center>';
	pop.getMainDiv().innerHTML = '<CENTER><BR><FONT COLOR=#550000><B>An error has ocurred:</b></font><BR><BR><DIV id=paretryErrMsg></div>\
		<BR><BR><B>Automatically retrying in <SPAN id=paretrySeconds></b></span> seconds ...<BR><BR><INPUT id=paretryCancel type=submit value="CANCEL Retry" \>';
	document.getElementById('paretryCancel').addEventListener ('click', doCancel, false);
	pop.show(true);

	document.getElementById('paretryErrMsg').innerHTML = errMsg;
	document.getElementById('paretrySeconds').innerHTML = seconds;
	var rTimer = setTimeout (doRetry, seconds*1000);
	countdown ();

	function countdown (){
		document.getElementById('paretrySeconds').innerHTML = seconds--;
		if (seconds > 0)
			cdTimer = setTimeout (countdown, 1000);
	}
	function doCancel(){
		clearTimeout (rTimer);
		clearTimeout (cdTimer);
		pop.destroy();
		onCancel ();
	}
	function doRetry (){
		clearTimeout (rTimer);
		clearTimeout (cdTimer);
		pop.show(false);
		onRetry();
	}
}

function implodeUrlArgs (obj){
	var a = [];
	for (var k in obj)
		a.push (k +'='+ encodeURI(obj[k]) );
	return a.join ('&');    
}

//NOTE: args can be either a string which will be appended as is to url or an object of name->values
function addUrlArgs (url, args){
	if (!args)
		return url;
	if (url.indexOf('?') < 0)
		url += '?';
	else if (url.substr(url.length-1) != '&')
		url += '&';    
	if (matTypeof(args == 'object'))
		return url + implodeUrlArgs (args);    
	return url + args;
}

//emulate protoype's Ajax.Request ...
function AjaxRequest (url, opts){
	var headers = {
			'X-Requested-With': 'XMLHttpRequest',
			'X-Prototype-Version': '1.6.1',
			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
	};
	var ajax = null;

	if (window.XMLHttpRequest)
		ajax=new XMLHttpRequest();
	else
		ajax=new ActiveXObject("Microsoft.XMLHTTP");

	if (opts.method==null || opts.method=='')
		method = 'GET';
	else
		method = opts.method.toUpperCase();  

	if (method == 'POST'){
		headers['Content-type'] = 'application/x-www-form-urlencoded; charset=UTF-8';
	} else if (method == 'GET'){
		addUrlArgs (url, opts.parameters);
	}

	ajax.onreadystatechange = function(){
//		['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; states 0-4
		if (ajax.readyState==4) {
			if (ajax.status >= 200 && ajax.status < 305)
				if (opts.onSuccess) opts.onSuccess(ajax);
				else
					if (opts.onFailure) opts.onFailure(ajax);
		} else {
			if (opts.onChange) opts.onChange (ajax);
		}
	}  

	ajax.open(method, url, true);   // always async!

	for (var k in headers)
		ajax.setRequestHeader (k, headers[k]);
	if (matTypeof(opts.requestHeaders)=='object')
		for (var k in opts.requestHeaders)
			ajax.setRequestHeader (k, opts.requestHeaders[k]);

	if (method == 'POST'){
		var a = [];
		for (k in opts.parameters)
			a.push (k +'='+ opts.parameters[k] );
		ajax.send (a.join ('&'));
	} else               {
		ajax.send();
	}
}   


function MyAjaxRequest (url, o, noRetryX){
	if (DEBUG_TRACE) logit (" 0 myAjaxRequest: "+ url +"\n" + inspect (o, 2, 1));
	var opts = unsafeWindow.Object.clone(o);
	var wasSuccess = o.onSuccess;
	var wasFailure = o.onFailure;
	var retry = 0;
	var delay = 5;
	var noRetry = noRetry===true?true:false;
	opts.onSuccess = mySuccess;
	opts.onFailure = myFailure;

	new AjaxRequest(url, opts);
	return;

	function myRetry(){
		++retry;
		new AjaxRequest(url, opts);
		delay = delay * 1.25;
	}
	function myFailure(){
		var o = {};
		o.ok = false;
		o.errorMsg = "AJAX Communication Failure";
		wasFailure (o);
	}
	function mySuccess (msg){
		var rslt = eval("(" + msg.responseText + ")");
		var x;
		if (window.EmulateAjaxError){
			rslt.ok = false;  
			rslt.error_code=8;
		}
		if (rslt.ok){
			if (rslt.updateSeed)
				unsafeWindow.update_seed(rslt.updateSeed);
			wasSuccess (rslt);
			return;
		}
		rslt.errorMsg = unsafeWindow.printLocalError((rslt.error_code || null), (rslt.msg || null), (rslt.feedback || null));
		if ( (x = rslt.errorMsg.indexOf ('<br><br>')) > 0)
			rslt.errorMsg = rslt.errorMsg.substr (0, x-1);
		if (!noRetry && (rslt.error_code==0 ||rslt.error_code==8 || rslt.error_code==1 || rslt.error_code==3)){
			dialogRetry (rslt.errorMsg, delay, function(){myRetry()}, function(){wasSuccess (rslt)});
		} else {
			wasSuccess (rslt);
		}
	}
}

//returns: 'neutral', 'friendly', or 'hostile'
function getDiplomacy (aid) {
	if (Seed.allianceDiplomacies == null)
		return 'neutral';
	if (Seed.allianceDiplomacies.friendly && Seed.allianceDiplomacies.friendly['a'+aid] != null)
		return 'friendly';
	if (Seed.allianceDiplomacies.hostile && Seed.allianceDiplomacies.hostile['a'+aid] != null)
		return 'hostile';
	return 'neutral';
};

function getMyAlliance (){
	if (Seed.allianceDiplomacies==null || Seed.allianceDiplomacies.allianceName==null)
		return [0, 'None'];
	else
		return [Seed.allianceDiplomacies.allianceId, Seed.allianceDiplomacies.allianceName];
}

function distance (d, f, c, e) {
	var a = 750;
	var g = a / 2;
	var b = Math.abs(c - d);
	if (b > g)
		b = a - b;
	var h = Math.abs(e - f);
	if (h > g)
		h = a - h;
	return Math.round(100 * Math.sqrt(b * b + h * h)) / 100;
};


//returns {count, maxlevel}
function getCityBuilding (cityId, buildingId){
	var b = Seed.buildings['city'+cityId];
	var ret = {count:0, maxLevel:0};
	for (var i=1; i<33; i++){
		if (b['pos'+i] && b['pos'+i][0] == buildingId){
			++ret.count;
			if (parseInt(b['pos'+i][1]) > ret.maxLevel)
				ret.maxLevel = parseInt(b['pos'+i][1]);
		}
	}
	return ret;
}

//example: http://www150.kingdomsofcamelot.com
var myServerId = null;
function getServerId() {
	if (myServerId == null){
		var m=/^[a-zA-Z]+([0-9]+)\./.exec(document.location.hostname);
		if (m)
			myServerId = m[1];
		else
			myServerId = '??';
	}
	return myServerId;
}

function logit (msg){
	var now = new Date();
	GM_log (getServerId() +' @ '+ now.toTimeString().substring (0,8) +'.' + now.getMilliseconds() +': '+  msg);
}



/************ DEBUG WIN *************/
var debugWin = {
		popDebug : null,
		dbDefaultNot : 'tech,tutorial,items,quests,wilderness,wildDef,buildings,knights,allianceDiplomacies,appFriends,players',
		dbSelect : {},

		doit : function (){ 
			var t = debugWin;    

			function syncBoxes (){
				var div = document.getElementById('dbpoplist');
				for (var i=0; i<div.childNodes.length; i++){
					if (div.childNodes[i].type && div.childNodes[i].type=='checkbox'){
						var name=div.childNodes[i].name.substr(6);
						div.childNodes[i].checked = t.dbSelect[name];
					}
				} 
			}
			function clickedAll (){
				for (var k in t.dbSelect)
					t.dbSelect[k] = true;
				syncBoxes();
			}
			function clickedNone (){
				for (var k in t.dbSelect)
					t.dbSelect[k] = false;
				syncBoxes();
			}
			function clickedDefaults (){
				for (k in t.dbSelect)
					t.dbSelect[k] = true;
				var not = t.dbDefaultNot.split(',');
				for (var i=0; i<not.length; i++)
					t.dbSelect[not[i]] = false;
				syncBoxes();
			}
			function clickedShow (){
				var now = new Date();
				var myseed = unsafeWindow.Object.clone (Seed);
				var div = document.getElementById('dbpoplist');
				for (var i=0; i<div.childNodes.length; i++){
					if (div.childNodes[i].type && div.childNodes[i].type=='checkbox'){
						var name=div.childNodes[i].name.substr(6);
						if (!div.childNodes[i].checked)
							delete myseed[name];
					}
				} 
				WinLog.write ("seed @ "+ unixTime()  +" ("+ now +")\n\n"+ inspect (myseed, 8, 1));
				myseed=null;
			}

			if (t.popDebug == null){  
				t.popDebug = new CPopup ('db', 0, 0, 350,500, true);
				t.popDebug.getTopDiv().innerHTML = 'DEBUG';
				t.popDebug.getMainDiv().innerHTML = '<DIV><INPUT type=submit id=padbsuball value=ALL> &nbsp; <INPUT type=submit id=padbsubnone value=NONE> &nbsp; \
					<INPUT type=submit id=padbdefaults value=DEFAULTS> &nbsp; <INPUT type=submit id=padbsubdo value=SHOW></div>\
					<DIV id=padbpoplist style="max-height:400px; height:400px; overflow-y:auto"></div>';
				var div = document.getElementById('padbpoplist');
				for (var k in Seed)
					t.dbSelect[k] = true;
				var not = t.dbDefaultNot.split(',');
				for (var i=0; i<not.length; i++)
					t.dbSelect[not[i]] = false;
				var m = [];
				for (k in t.dbSelect){
					m.push ('<INPUT type=checkbox ');
					m.push ('name="dbpop_');
					m.push (k);
					m.push ('"> &nbsp; ');
					m.push (k);
					m.push ('<BR>');
				}
				div.innerHTML = m.join ('');
				document.getElementById('padbsuball').addEventListener('click', clickedAll, false);
				document.getElementById('padbsubnone').addEventListener('click', clickedNone, false);
				document.getElementById('padbdefaults').addEventListener('click', clickedDefaults, false);
				document.getElementById('padbsubdo').addEventListener('click', clickedShow, false);
				syncBoxes();
			}
			t.popDebug.show (true);
		},
}

function saveOptions (){
	var serverID = getServerId();
	setTimeout (function (){GM_setValue ('Options_'+serverID, JSON2.stringify(Options));}, 0);
}

function readOptions (){
	var serverID = getServerId();
	s = GM_getValue ('Options_'+serverID);
	if (s != null){
		opts = JSON2.parse (s);
		for (k in opts)
			Options[k] = opts[k];
	}
}

function readGlobalOptions (){
	GlobalOptions = JSON2.parse (GM_getValue ('Options_??', '{}'));
}

function createButton (label){
	var a=document.createElement('a');
	a.className='button20';
	a.innerHTML='<span style="color: #ff6">'+ label +'</span>';
	return a;
}

function AddMainTabLink(text, eventListener, mouseListener) {
	var a = createButton (text);
	a.className='tab';
	var tabs=document.getElementById('main_engagement_tabs');
	if(!tabs) {
		tabs=document.getElementById('topnav_msg');
		if (tabs)
			tabs=tabs.parentNode;
	}
	if (tabs) {
		var e = tabs.parentNode;
		var gmTabs = null;
		for (var i=0; i<e.childNodes.length; i++){
			var ee = e.childNodes[i];
			if (ee.tagName && ee.tagName=='DIV' && ee.className=='tabs_engagement' && ee.id!='main_engagement_tabs'){
				gmTabs = ee;
				break;
			}
		}
		if (gmTabs == null){
			gmTabs = document.createElement('div');
			gmTabs.className='tabs_engagement';
			gmTabs.style.background='none';
			tabs.parentNode.insertBefore (gmTabs, tabs);
			gmTabs.style.whiteSpace='nowrap';
			gmTabs.style.width='735px';
			gmTabs.lang = 'en_PB';
		}
		gmTabs.appendChild(a);
		a.addEventListener('click',eventListener, false);
		if (mouseListener != null)
			a.addEventListener('mousedown',mouseListener, true);
		return a;
	}
	return null;
}


unsafeWindow.pcGotoMap = function (x, y){
	hideMe ();
	setTimeout (function (){ 
		document.getElementById('mapXCoor').value = x;
		document.getElementById('mapYCoor').value = y;
		unsafeWindow.reCenterMapWithCoor();
		var a = document.getElementById("mod_views").getElementsByTagName("a");
		for (var b = 0; b < a.length; b++) {
			a[b].className = ""
		}
		document.getElementById('mod_views_map').className = "sel";
		document.getElementById("maparea_city").style.display = 'none';
		document.getElementById("maparea_fields").style.display = 'none';
		document.getElementById("maparea_map").style.display = 'block';
		unsafeWindow.tutorialClear()
	}, 0);
};

/**********************************************************************************/
var CalterUwFunc = function (funcName, findReplace) {
	var t = this;
	this.isEnabled = false;
	this.isAvailable = isAvailable;
	this.setEnable = setEnable;
	this.funcName = funcName;
	this.funcOld = unsafeWindow[funcName];  
	this.funcNew = null;
	try {
		var funcText = unsafeWindow[funcName].toString();
		var rt = funcText.replace ('function '+ funcName, 'function');
		for (var i=0; i<findReplace.length; i++){
			x = rt.replace(findReplace[i][0], findReplace[i][1]);
			if (x == rt)
				return false;
			rt = x;
		}
		this.funcNew = rt;
	} catch (err) {
	}

	function setEnable (tf){
		if (t.funcNew == null)
			return;
		if (t.isEnabled != tf){
			if (tf){
				var scr=document.createElement('script');
				scr.innerHTML = funcName +' = '+ t.funcNew;
				document.body.appendChild(scr);
				setTimeout ( function (){document.body.removeChild(scr);}, 0);
				t.isEnabled = true;
			} else {
				unsafeWindow[t.funcName] = t.funcOld;
				t.isEnabled = false;
			}
		}
	}
	function isAvailable (){
		if (t.funcNew == null)
			return false;
		return true;
	}
};


function makeButton20 (label){
	var a = document.createElement('a');
	a.className = "button20 ptButton20";
	var s = document.createElement('span');
	s.innerHTML = label;
	a.appendChild (s);
	return a;
}

function strButton20 (label, tags){
	if (tags == null)
		tags = '';
	return ('<TABLE class=ptNoPad><TR><TD><A class="button20 ptButton20" '+ tags +'><SPAN>'+ label +'</span></a></td></tr></table>' );
}

function reloadKOC (){
	var goto = 'http://apps.facebook.com/kingdomsofcamelot/?s='+getServerId();
	var t = '<FORM target="_top" action="'+ goto +'" method=get><INPUT id=xxButReload type=submit value=RELOAD></form>';
	var e = document.createElement ('div');
	e.innerHTML = t;
	document.body.appendChild (e);
	setTimeout (function (){document.getElementById('xxButReload').click();}, 0);
	return;
}

function htmlSelector (valNameObj, curVal, tags){
	var m = [];
	m.push ('<SELECT');
	if (tags){
		m.push (' ');
		m.push (tags);
	}  
	for (var k in valNameObj){
		m.push ('><OPTION ');
		if (k == curVal)
			m.push ('SELECTED ');
		m.push ('value="');
		m.push (k);
		m.push ('">');
		m.push (valNameObj[k]);
		m.push ('</option>');
	}
	m.push ('</select>');
	return m.join ('');
}

function cityStatusString (cs){
	if (cs==4)
		return 'Vacation';
	if (cs==3)
		return 'Truce';
	if (cs==2)
		return 'Beg Protection';
	return 'Normal';
}    

//Simple method, as if it were typed in thru DOM
function sendChat (msg){
	document.getElementById ("mod_comm_input").value = msg;
	unsafeWindow.Chat.sendChat ();
}

//works well, but message is not echoed back to local client
Chat = {
		params : null,

		sendWhisper : function (msg, who, notify){
			this.params = unsafeWindow.Object.clone(unsafeWindow.g_ajaxparams);
			this.params.ctype = 3;
			this.params.name = who;
			this._sendit (msg, notify);
		},

		sendGlobal : function (msg, notify){
			this.params = unsafeWindow.Object.clone(unsafeWindow.g_ajaxparams);
			this.params.ctype = 1;
			this._sendit (msg, notify);
		},

		sendAlliance : function (msg, notify){
			this.params = unsafeWindow.Object.clone(unsafeWindow.g_ajaxparams);
			this.params.ctype = 2;
			this._sendit (msg, notify);
		},

		_sendit : function (msg, notify){
			function strip(s) {
				return s.replace(/^\s+/, '').replace(/\s+$/, '');
			}
			this.params.comment = strip (msg);
			new MyAjaxRequest(unsafeWindow.g_ajaxpath + "ajax/sendChat.php" + unsafeWindow.g_ajaxsuffix, {
				method: "post",
				parameters: this.params,
				onSuccess: function(transport) {
					if (notify)
						notify ();
				},
				onFailure: function(transport) {
					if (notify)
						notify ();
				}
			});
		},
}



/************  LIB classes/functions .... **************/

DebugTimer = {
		startTime : 0,
		start : function (){
			now = new Date();
			DebugTimer.startTime = now.getTime();
		},
		getMillis : function (){
			now = new Date();
			return now.getTime() - DebugTimer.startTime;
		},
		display : function (label, noReset){
			now = new Date();
			elapsed = now.getTime() - DebugTimer.startTime;
			logit (label +": "+ elapsed/1000);
			if (noReset===null || !noReset)
				DebugTimer.startTime = now.getTime();
		},
};


function debugPos  (e){
	return '['+ e.tagName +'] client - offset: '+ e.clientLeft +','+ e.clientTop +','+ e.clientWidth +','+ e.clientHeight
	+' - '+ e.offsetLeft +','+ e.offsetTop +','+ e.offsetWidth +','+ e.offsetHeight +' '+ e +' --OP--> '+ e.offsetParent;
}

function CwaitForElement (id, timeout, notify){
	this.check = check;
	this.end = new Date().getTime() + timeout;
	var t = this;
	this.check();
	function check(){
		if (document.getElementById (id))
			notify (true);
		else if (new Date().getTime() > t.end)
			notify (false);
		else
			setTimeout (t.check, 250);
	}
}

function clickWin (win,obj,evtName) {
	var evt = win.document.createEvent("MouseEvents");
	evt.initMouseEvent(evtName, true, true, win, 0, 0, 0, 0, 0, false, false, false, false, 0, null);
	return !obj.dispatchEvent(evt);
}

function debugElement  (e){
	var x = unsafeWindow.Object.clone (e.wrappedJSObject);
	x.innerHTML = '';
	x.innerText = '';
	x.textContent = '';
	return inspect (x, 1, 1);
}     

function getClientCoords(e){
	if (e==null)
		return {x:null, y:null, width:null, height:null};
		var x=0, y=0;
		ret = {x:0, y:0, width:e.clientWidth, height:e.clientHeight};
		while (e.offsetParent != null){
			ret.x += e.offsetLeft;
			ret.y += e.offsetTop;
			e = e.offsetParent;
		}
		return ret;
}

function DOMtree (e, levels){
	var m = [];
	level (e, levels, 0);

	function level (e, levels, cur){
		try {        
			for (var i=0; i<cur; i++)
				m.push('  ');
			if (!e.tagName)
				m.push ('?');
			else
				m.push (e.tagName);
			if (e.id){
				m.push (' id=');
				m.push (e.id);
			}
			if (e.name){
				m.push (' name=');
				m.push (e.name);
			}
			if (e.className){
				m.push (' class=');
				m.push (e.className);
			}
			if (e.style && e.style.display && e.style.display.indexOf('none')>0)
				m.push (' hidden');
			m.push ('\n');
			if (cur < levels){
				for (var c=0; c<e.childNodes.length; c++){
					level (e.childNodes[c], levels, cur+1);
				}
			}
		} catch (e) {
			m.push ('UNAVAILBLE!\n');
		}
	}
	return m.join('');  
}

function parseIntNan (n){
	x = parseInt(n, 10);
	if (isNaN(x))
		return 0;
	return x; 
}
function parseIntZero (n){
	n = n.trim();
	if (n == '')
		return 0;
	return parseInt(n, 10);
}




var WinManager = {
		wins : {},    // prefix : CPopup obj

		get : function (prefix){
			var t = WinManager;
			return t.wins[prefix];
		},

		add : function (prefix, pop){
			var t = WinManager;
			t.wins[prefix] = pop;
			if (unsafeWindow.cpopupWins == null)
				unsafeWindow.cpopupWins = {};
			unsafeWindow.cpopupWins[prefix] = pop;
		},

		delete : function (prefix){
			var t = WinManager;
			delete t.wins[prefix];
			delete unsafeWindow.cpopupWins[prefix];
		}    
}


//creates a 'popup' div
//prefix must be a unique (short) name for the popup window
function CPopup (prefix, x, y, width, height, enableDrag, onClose) {
	var pop = WinManager.get(prefix);
	if (pop){
		pop.show (false);
		return pop;
	}
	this.BASE_ZINDEX = 111111;

	// protos ...
	this.show = show;
	this.toggleHide = toggleHide;
	this.getTopDiv = getTopDiv;
	this.getMainDiv = getMainDiv;
	this.getLayer = getLayer;
	this.setLayer = setLayer;
	this.setEnableDrag = setEnableDrag;
	this.getLocation = getLocation;
	this.setLocation = setLocation;
	this.focusMe = focusMe;
	this.unfocusMe = unfocusMe;
	this.centerMe = centerMe;
	this.destroy = destroy;
	this.autoHeight = autoHeight;

	// object vars ...
	this.div = document.createElement('div');
	this.prefix = prefix;
	this.onClose = onClose;

	var t = this;
	this.div.className = 'CPopup '+ prefix +'_CPopup';
	this.div.id = prefix +'_outer';
	this.div.style.background = "#fff";
	this.div.style.zIndex = this.BASE_ZINDEX        // KOC modal is 100210 ?
	this.div.style.display = 'none';
	this.div.style.width = width + 'px';
	this.div.style.height = height + 'px';
	this.div.style.maxHeight = height + 'px';
	this.div.style.overflowY = 'hidden';
	this.div.style.position = "absolute";
	this.div.style.top = y +'px';
	this.div.style.left = x + 'px';

	if (CPopUpTopClass==null)
		topClass = 'CPopupTop '+ prefix +'_CPopupTop';
	else
		topClass = CPopUpTopClass +' '+ prefix +'_'+ CPopUpTopClass;

	var m = '<TABLE cellspacing=0 width=100% height=100%><TR id="'+ prefix +'_bar" class="'+ topClass +'"><TD width=99% valign=bottom><SPAN id="'+ prefix +'_top"></span></td>\
	<TD id='+ prefix +'_X align=right valign=middle onmouseover="this.style.cursor=\'pointer\'" style="color:#fff; background:#333; font-weight:bold; font-size:14px; padding:0px 5px">X</td></tr>\
	<TR><TD height=100% valign=top class="CPopMain '+ prefix +'_CPopMain" colspan=2 id="'+ prefix +'_main"></td></tr></table>';
	document.body.appendChild(this.div);
	this.div.innerHTML = m;
	document.getElementById(prefix+'_X').addEventListener ('click', e_XClose, false);
	this.dragger = new CWinDrag (document.getElementById(prefix+'_bar'), this.div, enableDrag);

	this.div.addEventListener ('mousedown', e_divClicked, false);
	WinManager.add(prefix, this);

	function e_divClicked (){
		t.focusMe();
	}  
	function e_XClose (){
		t.show(false);
		if (t.onClose != null)
			t.onClose();
	}
	function autoHeight (onoff){
		if (onoff)
			t.div.style.height = '';  
		else
			t.div.style.height = t.div.style.maxHeight;
	}
	function focusMe (){
		t.setLayer(5);
		for (k in unsafeWindow.cpopupWins){
			if (k != t.prefix)
				unsafeWindow.cpopupWins[k].unfocusMe(); 
		}
	}
	function unfocusMe (){
		t.setLayer(-5);
	}
	function getLocation (){
		return {x: parseInt(this.div.style.left), y: parseInt(this.div.style.top)};
	}
	function setLocation (loc){
		t.div.style.left = loc.x +'px';
		t.div.style.top = loc.y +'px';
	}
	function destroy (){
		document.body.removeChild(t.div);
		WinManager.delete (t.prefix);
	}
	function centerMe (parent){
		if (parent == null){
			var coords = getClientCoords(document.body);
		} else
			var coords = getClientCoords(parent);
		var x = ((coords.width - parseInt(t.div.style.width)) / 2) + coords.x;
		var y = ((coords.height - parseInt(t.div.style.height)) / 2) + coords.y;
		t.div.style.left = x +'px';
		t.div.style.top = y +'px';
	}
	function setEnableDrag (tf){
		t.dragger.setEnable(tf);
	}
	function setLayer(zi){
		t.div.style.zIndex = ''+ (this.BASE_ZINDEX + zi);
	}
	function getLayer(){
		return parseInt(t.div.style.zIndex) - this.BASE_ZINDEX;
	}
	function getTopDiv(){
		return document.getElementById(this.prefix+'_top');
	}
	function getMainDiv(){
		return document.getElementById(this.prefix+'_main');
	}
	function show(tf){
		if (tf){
			t.div.style.display = 'block';
			t.focusMe ();
		} else {
			t.div.style.display = 'none';
		}
		return tf;
	}
	function toggleHide(t){
		if (t.div.style.display == 'block') {
			return t.show (false);
		} else {
			return t.show (true);
		}
	}
}

function CWinDrag (clickableElement, movingDiv, enabled) {
	var t=this;
	this.setEnable = setEnable;
	this.setBoundRect = setBoundRect;
	this.debug = debug;
	this.dispEvent = dispEvent;
	this.lastX = null;
	this.lastY = null;
	this.enabled = true;
	this.moving = false;
	this.theDiv = movingDiv;
	this.body = document.body;
	this.ce = clickableElement;
	this.moveHandler = new CeventMove(this).handler;
	this.outHandler = new CeventOut(this).handler;
	this.upHandler = new CeventUp(this).handler;
	this.downHandler = new CeventDown(this).handler;
	this.clickableRect = null;
	this.boundRect = null;
	this.bounds = null;
	this.enabled = false;
	if (enabled == null)
		enabled = true;
	this.setEnable (enabled);

	function setBoundRect (b){    // this rect (client coords) will not go outside of current body
		this.boundRect = boundRect;
		this.bounds = null;
	}

	function setEnable (enable){
		if (enable == t.enabled)
			return;
		if (enable){
			clickableElement.addEventListener('mousedown',  t.downHandler, false);
			t.body.addEventListener('mouseup', t.upHandler, false);
		} else {
			clickableElement.removeEventListener('mousedown', t.downHandler, false);
			t.body.removeEventListener('mouseup', t.upHandler, false);
		}
		t.enabled = enable;
	}

	function CeventDown (that){
		this.handler = handler;
		var t = that;
		function handler (me){
			if (t.bounds == null){
				t.clickableRect = getClientCoords(clickableElement);
				t.bodyRect = getClientCoords(document.body);
				if (t.boundRect == null)
					t.boundRect = t.clickableRect;
				t.bounds = {top:10-t.clickableRect.height, bot:t.bodyRect.height-25, left:40-t.clickableRect.width, right:t.bodyRect.width-25};
			}
			if (me.button==0 && t.enabled){
				t.body.addEventListener('mousemove', t.moveHandler, true);
				t.body.addEventListener('mouseout', t.outHandler, true);
				t.lastX = me.clientX;
				t.lastY = me.clientY;
				t.moving = true;
			}
		}
	}

	function CeventUp  (that){
		this.handler = handler;
		var t = that;
		function handler (me){
			if (me.button==0 && t.moving)
				_doneMoving(t);
		}
	}

	function _doneMoving (t){
		t.body.removeEventListener('mousemove', t.moveHandler, true);
		t.body.removeEventListener('mouseout', t.outHandler, true);
		t.moving = false;
	}

	function CeventOut  (that){
		this.handler = handler;
		var t = that;
		function handler (me){
			if (me.button==0){
				t.moveHandler (me);
			}
		}
	}

	function CeventMove (that){
		this.handler = handler;
		var t = that;
		function handler (me){
			if (t.enabled && !t.wentOut){
				var newTop = parseInt(t.theDiv.style.top) + me.clientY - t.lastY;
				var newLeft = parseInt(t.theDiv.style.left) + me.clientX - t.lastX;
				if (newTop < t.bounds.top){     // if out-of-bounds...
					newTop = t.bounds.top;
					_doneMoving(t);
				} else if (newLeft < t.bounds.left){
					newLeft = t.bounds.left;
					_doneMoving(t);
				} else if (newLeft > t.bounds.right){
					newLeft = t.bounds.right;
					_doneMoving(t);
				} else if (newTop > t.bounds.bot){
					newTop = t.bounds.bot;
					_doneMoving(t);
				}
				t.theDiv.style.top = newTop + 'px';
				t.theDiv.style.left = newLeft + 'px';
				t.lastX = me.clientX;
				t.lastY = me.clientY;
			}
		}
	}

	function debug  (msg, e){
		logit ("*************** "+ msg +" ****************");
		logit ('clientWidth, Height: '+ e.clientWidth +','+ e.clientHeight);
		logit ('offsetLeft, Top, Width, Height (parent): '+ e.offsetLeft +','+ e.offsetTop +','+ e.offsetWidth +','+ e.offsetHeight +' ('+ e.offsetParent +')');
		logit ('scrollLeft, Top, Width, Height: '+ e.scrollLeft +','+ e.scrollTop +','+ e.scrollWidth +','+ e.scrollHeight);
	}

	function dispEvent (msg, me){
		logit (msg + ' Button:'+ me.button +' Screen:'+ me.screenX +','+ me.screenY +' client:'+  me.clientX +','+ me.clientY +' rTarget: '+ me.relatedTarget);
	}
}

function inspect(obj, maxLevels, level, doFunctions){
	var str = '', type, msg;
	if(level == null)  level = 0;
	if(maxLevels == null) maxLevels = 1;
	if(maxLevels < 1)
		return 'Inspect Error: Levels number must be > 0';
	if(obj == null)
		return 'ERROR: Object is NULL\n';
	var indent = '';
	for (var i=0; i<level; i++)
		indent += '  ';
	for(property in obj) {
		try {
			type =  matTypeof(obj[property]);
			if (doFunctions==true && (type == 'function')){
				str += indent + '(' + type + ') ' + property + "[FUNCTION]\n";
			} else if (type != 'function') {
				str += indent + '(' + type + ') ' + property + ( (obj[property]==null)?(': null'):('')) +' = '+ obj[property] +"\n";
			}
			if((type=='object' || type=='array') && (obj[property] != null) && (level+1 < maxLevels))
				str += inspect(obj[property], maxLevels, level+1, doFunctions);  // recurse
		}
		catch(err) {
			// Is there some properties in obj we can't access? Print it red.
			if(typeof(err) == 'string') msg = err;
			else if(err.message)        msg = err.message;
			else if(err.description)    msg = err.description;
			else                        msg = 'Unknown';
			str += '(Error) ' + property + ': ' + msg +"\n";
		}
	}
	str += "\n";
	return str;
}

Array.prototype.compare = function(testArr) {
	if (this.length != testArr.length) return false;
	for (var i = 0; i < testArr.length; i++) {
		if (this[i].compare) { 
			if (!this[i].compare(testArr[i])) return false;
		}
		if (this[i] !== testArr[i]) return false;
	}
	return true;
}

function objectName (o){
	var s = o.toString();
	return s.substr(7,s.length-8);
}

function matTypeof (v){
	if (typeof (v) == 'object'){
		if (!v)
			return 'null';
//		else if (unsafeWindow.Object.prototype.toString.apply(v) === '[object Array]')
		else if (v.constructor.toString().indexOf("Array")>=0 && typeof(v.splice)=='function')
			return 'array';
		else return 'object';
	}
	return typeof (v);
}


function tbodyScroller (tbody, maxHeight){  
	tbody.style.maxHeight = '';
	tbody.style.height = '';
	tbody.style.overflowX = 'hidden';
	if (parseInt(tbody.clientHeight) > maxHeight){
		tbody.style.height = maxHeight + 'px';
		tbody.style.maxHeight = maxHeight + 'px';
		tbody.style.overflowY = 'auto';
	}
}
function getRemainingHeight (e, cont){
	var ec = getClientCoords(e);
	var cc = getClientCoords(cont);
	return cont.clientHeight - (ec.y - cc.y);
}


function addCommasInt(n){
	nStr = parseInt(n) + '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(nStr)) {
		nStr = nStr.replace(rgx, '$1' + ',' + '$2');
	}
	return nStr;
}

function addCommas(nStr){
	nStr += '';
	x = nStr.split('.');
	x1 = x[0];
	x2 = x.length > 1 ? '.' + x[1] : '';
	var rgx = /(\d+)(\d{3})/;
	while (rgx.test(x1)) {
		x1 = x1.replace(rgx, '$1' + ',' + '$2');
	}
	return x1 + x2;
}

function unixTime (){
	return parseInt (new Date().getTime() / 1000) + unsafeWindow.g_timeoff;
}
function htmlOptions (a, curVal){
	m = '';
	for (k in a)
		m += '<OPTION value="'+ k +'"'+ (k==curVal?' SELECTED':'')  +'>'+ a[k] +'</option>';
	return m;
}
function getFunctionName (func){
	var name=/\W*function\s+([\w\$]+)\(/.exec(func);
	if (!name)
		return '';
	return name[1];
}

function findAllBetween (txt, find1, find2){
	var m = [];
	var last = 0;
	while ( (i1=txt.indexOf(find1, last))>=0 && (i2=txt.indexOf (find2, i1))>=0 ) {
		m.push (txt.substring(i1+find1.length, i2));
		last = i2 + find2.length;
	}
	return m;
}

function strUpTo (s, find){
	var i = s.indexOf(find);
	if (i > 0)
		return s.substr(0, i);
	return s;
}

/********
 Xd Xh
 Xh Xm
 Xm Xs
 Xs
 ********/
function timestrShort(time) {
	time = parseInt (time);
	if (time > 86400){
		var m = [];
		time /= 3600;
		m.push (parseInt(time/24)); 
		m.push ('d ');
		m.push (parseInt(time%24)); 
		m.push ('h ');
		return m.join ('');    
	} else
		return timestr (time);
}

/**********************
 part       full
 Xd Xh Xm   Xd Xh Xm Xs
 Xh Xm      Xh Xm Xs
 Xm Xs      Xm Xs
 Xs         Xs
 **********************/
function timestr(time, full) {
	time = parseInt (time);
	var m = [];
	var t = time;
	if (t < 61)
		return  t + 's';
	if (t > 86400){
		m.push (parseInt(t/86400)); 
		m.push ('d ');
		t %= 86400;
	}  
	if (t>3600 || time>3600){
		m.push (parseInt(t/3600)); 
		m.push ('h ');
		t %= 3600;
	}  
	m.push (parseInt(t/60)); 
	m.push ('m');
	if (full || time<=3600 ){
		m.push (' ');
		m.push (t%60);
		m.push ('s');  
	}
	return m.join ('');
}

/************  LIB singletons .... **************/
//TODO: fix REopening window
var WINLOG_MAX_ENTRIES = 1000;     // TODO
var WinLog = {
		state : null,
		win: null,
		eOut : null,
		lastE : null,
		enabled : true,
		reverse : true,
		busy : false,
		isOpening : false,

		open : function (){
			var t = WinLog;

			function eventButClear(){
				var t = WinLog;
				t.lastE = null;
				t.eOut.innerHTML ='';
			}
			function eventButReverse(){
				var t = WinLog;
				if (t.busy)
					return;
				t.busy = true;
				if (t.reverse){
					t.win.document.getElementById('wlRev').value= 'Top';
					t.reverse = false;
				} else{
					t.win.document.getElementById('wlRev').value= 'Bottom';
					t.reverse = true;
				}
				var n = t.eOut.childNodes.length;
				if (n < 2)
					return;
				for (i=n-2; i>=0; i--){
					t.eOut.appendChild (t.eOut.childNodes[i]);
				}
				t.busy = false;
			}

			if (!t.win || t.win.closed){
				t.isOpening = true;  
//				Firefox bug??? It appears as if a new thread is started on open, withOUT reusing same window? huh?
				t.win = window.open('', 'uwtrace', 'top=30,left=0,width=750,height=700,scrollbars=no,location=no,menubar=no,directories=no,status=no');
				t.isOpening = false; 
				t.state = null; 
			}

			if (t.state == null){
				t.win.document.body.innerHTML = '<STYLE>pre{margin:0px} hr{margin:3px; height:1px; border:0px; color:#cee; background-color:#cee}</style>\
					<BODY style="margin:0px; padding:0px; border:none">\
					<DIV id=winlogtop style="background-color:#d0d0d0; margin:0px; padding:0px; border:1px solid">\
					<INPUT id=wlClear type=submit value="Clear"> &nbsp; <INPUT id=wlRev type=submit value="Bottom"></div>\
					<DIV id=wlOut style="overflow-y:auto; height:100%; max-height:100%"></div></body>';
				t.win.document.getElementById('wlClear').addEventListener('click', eventButClear, false);
				t.win.document.getElementById('wlRev').addEventListener('click', eventButReverse, false);
				t.eOut =  t.win.document.getElementById('wlOut');
				t.lastE = null;
				t.state = 1;
			}
		},

		writeText : function (msg){
			var t = WinLog;
			if (!t.enabled || t.isOpening)
				return;
			t.write (t.gtlt(msg));
		},

		write : function (msg){
			var t = WinLog;
			if (!t.enabled || t.isOpening)
				return;
			t.open();
			var te = document.createElement('pre');
			var now = new Date();
			var m = [];
			var millis = now.getMilliseconds();
			m.push (now.toTimeString().substring (0,8));
			m.push ('.');
			if (millis<100)
				m.push('0');
			if (millis<10)
				m.push('0');
			m.push(millis);
			m.push (': ');
			m.push (msg);
			te.innerHTML = m.join('');
			if (t.reverse){
				if (t.lastE == null){
					t.eOut.appendChild(te);
					t.lastE = te;
				} else {
					t.eOut.insertBefore(te, t.lastE);
				}
				var hr = document.createElement('hr');
				t.eOut.insertBefore(hr, te);
				t.lastE = hr;
			} else {
				t.eOut.appendChild(te);
				t.eOut.appendChild(document.createElement('hr'));
			}
		},

		gtlt : function (s){
			s = s.toString().replace ('<', '&lt;', 'g');
			return s.replace ('>', '&gt;', 'g');
		},

};


/*********************************** Gifts TAB ***********************************/
function explodeUrlArgs (url){
	var i = url.indexOf ('?');
	var a = url.substr(i+1).split ('&'); 
	var args = {};
	for (i=0; i<a.length; i++){
		var s = a[i].split ('=');
		args[s[0]] = s[1];
	}
	return args;
}


//returns: page text or null on comm error
function GM_AjaxPost (url, args, notify, label){
	if (ENABLE_GM_AJAX_TRACE) WinLog.writeText ('GM_AjaxPost ('+ label +'): ' + url +'\n'+ inspect (args, 5, 1));
	GM_xmlhttpRequest({
		method: "post",
		url: url,
		data: implodeUrlArgs(args),
		headers: { "Content-Type": "application/x-www-form-urlencoded", 'X-Requested-With': 'XMLHttpRequest', 'X-Prototype-Version': '1.6.1',
			'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' },
			onload: function (rslt) {
				if (ENABLE_GM_AJAX_TRACE) WinLog.writeText ( 'GM_AjaxPost.onLoad ('+ label +'):\n '  + inspect (rslt, 6, 1));   
				notify (rslt.responseText);
			},
			onerror: function () {
				notify (null);
			},
	});  
}
//returns: page text or null on comm error
function GM_AjaxGet (url, args, notify, label){
	if (ENABLE_GM_AJAX_TRACE) WinLog.writeText ('GM_AjaxGet ('+ label +'): ' + url);
	GM_xmlhttpRequest({
		method: "get",
		url: addUrlArgs(url, args),
		onload: function (rslt) {
			if (ENABLE_GM_AJAX_TRACE) WinLog.writeText ( 'GM_AjaxGet.onLoad ('+ label +')  len='+ rslt.responseText.length +':\n '  + inspect (rslt, 6, 1));   
			notify (rslt.responseText);
		},
		onerror: function () {
			notify (null);
		},
	});  
}